﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using StoreDemo.Domain.Data;
using StoreDemo.Domain.Entities;
using StoreDemo.Domain.Services;

namespace StoreDemo.Domain.Test.Unit.Services.Products
{
    [TestClass]
    public class ProductServiceTests
    {
        [TestMethod]
        public void GetAll_ReturnsAllProductsFromTheRepository()
        {
            //Arrange
            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            IQueryable<Product> products = new List<Product>().AsQueryable();
            productRepository.Stub(x => x.All).Return(products);
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            IEnumerable<Product> returnedProducts = productService.GetAllProducts();

            //Assert
            CollectionAssert.AreEqual(products.ToList(), returnedProducts.ToList());
        }

        [TestMethod]
        public void Get_ReturnsProductByProductId()
        {
            //Arrange
            Product expectedProduct = new Product {ProductId = 12345};
            IList<Product> products = new List<Product>
                                          {
                                              new Product {ProductId = 12031}, expectedProduct, new Product {ProductId = 12313}
                                          };

            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            productRepository.Stub(x => x.All).Return(products.AsQueryable());
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            Product returnedProduct = productService.Get(expectedProduct.ProductId);

            //Assert
            Assert.AreSame(expectedProduct, returnedProduct);
        }

        [TestMethod]
        public void Save_SetsTheCreatedPropertyOnTheProduct()
        {
            //Arrange
            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            Product product = new Product();

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Save(product);

            //Assert
            Assert.AreNotEqual(DateTime.MinValue, product.Created);
        }

        [TestMethod]
        public void Save_InsertsTheNewProductToTheRepository()
        {
            //Arrange
            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            Product product = new Product();

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Save(product);

            //Assert
            productRepository.AssertWasCalled(x => x.Insert(product));
        }

        [TestMethod]
        public void Save_CommitsTheUnitOfWork()
        {
            //Arrange
            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            Product product = new Product();

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Save(product);

            //Assert
            unitOfWork.AssertWasCalled(x => x.Save());
        }
        
        [TestMethod]
        public void Update_UpdatesTheSpecifiedProductFromTheRepository()
        {
            //Arrange
            Product product = new Product();

            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Update(product);

            //Assert
            productRepository.AssertWasCalled(x => x.Update(product));
        }

        [TestMethod]
        public void Update_CommitsTheUnitOfWork()
        {
            //Arrange
            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            Product product = new Product();

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Update(product);

            //Assert
            unitOfWork.AssertWasCalled(x => x.Save());
        }


        [TestMethod]
        public void Delete_DeletesTheSpecifiedProductFromTheRepository()
        {
            //Arrange
            const int idOfProductToBeDeleted = 29012321;

            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Delete(idOfProductToBeDeleted);

            //Assert
            productRepository.AssertWasCalled(x => x.Delete(idOfProductToBeDeleted));
        }

        [TestMethod]
        public void Delete_CommitsTheUnitOfWork()
        {
            //Arrange
            IUnitOfWorkFactory unitOfWorkFactory = MockRepository.GenerateStub<IUnitOfWorkFactory>();
            IUnitOfWork unitOfWork = MockRepository.GenerateStub<IUnitOfWork>();
            IRepository<Product> productRepository = MockRepository.GenerateStub<IRepository<Product>>();
            unitOfWork.Stub(x => x.GetRepository<Product>()).Return(productRepository);
            unitOfWorkFactory.Stub(x => x.GetUnitOfWork()).Return(unitOfWork);

            //Act
            IProductService productService = new ProductService(unitOfWorkFactory);
            productService.Delete(211231);

            //Assert
            unitOfWork.AssertWasCalled(x => x.Save());
        }
    }
}